<template>
  <div
    class="ui-grid"
    :style="gridStyle"
  >
    <div class="ui-grid__wrap" v-if="ready && showWrap" ref="wrap">
      <div class="ui-grid__header" ref="header">
        <div class="ui-grid__filter">
          <ui-filter ref="filter" :value="filterProps.value" :ready="filterReady" @change="handleFilterChange">
            <ui-filter-item
              class="ui-grid__filter-item"
              v-for="item in simpleFilterItems"
              :key="item.id"
              v-bind="item.props"
            >
              <component :is="handleRenderFilterItem(item)" :data-key="item.data && item.data.key"></component>
            </ui-filter-item>
          </ui-filter>
        </div>
        <div class="ui-grid__global-toolbar" v-if="globalToolbarProps && globalToolbarProps.items && globalToolbarProps.items.length">
          <grid-global-toolbar
            v-bind="globalToolbarProps"
          ></grid-global-toolbar>
        </div>
        <div class="ui-grid__more-toolbar" v-if="moreToolbarProps && moreToolbarProps.items && moreToolbarProps.items.length">
          <grid-more-toolbar
            v-bind="moreToolbarProps"
          ></grid-more-toolbar>
        </div>
      </div>
    </div>
    <div class="ui-grid__table" :style="tableStyle">
      <div class="ui-grid__batch" ref="batch">
        <transition @before-enter="handleBatchBeforeEnter" @after-leave="handleBatchAfterLeave">
            <div class="ui-grid__batch-inner" v-if="batchToolbarProps.items && !!selecteds.length">
              <grid-batch-toolbar
                :data="tableData"
                :selecteds="selecteds"
                v-bind="batchToolbarProps"
                @clear-selection="handleClearSelection"
                @batch-success="handleBatchSuccess"
              ></grid-batch-toolbar>
            </div>
        </transition>
      </div>
      <grid-table
        ref="table"
        :show-index="showIndex"
        :border="border"
        :id-field="innerIdField"
        :multiple="innerMultiple"
        :selectable="innerSelectable"
        :data="tableData"
        :height="tableHeight"
        :max-height="tableMaxHeight"
        :empty-text="emptyText"
        :show-summary="showSummary"
        :summary-method="summaryMethod"
        :show-pagination="showPagination"
        :page-index="tablePageIndex"
        :page-size="tablePageSize"
        :row-height="rowHeight"
        :parent-field="parentField"
        @loaded="handleDataLoaded"
        @selection-change="handleSelectionChange"
        @select-change="handleSelectChange"
      >
        <slot></slot>
        <vxe-column
          title="操作"
          fixed="right"
          field="__rowToolbar"
          :width="rowToolbarWidth"
          v-if="rowToolbar && rowToolbar.length"
        >
          <template slot-scope="{ row, $index }">
            <grid-row-toolbar :row="row" :row-index="$index" :items="rowToolbar"></grid-row-toolbar>
          </template>
        </vxe-column>
      </grid-table>
      <transition>
        <div
          class="ui-grid__loading"
          v-if="tableLoading"
        >
          <ui-loading type="primary" size="medium"></ui-loading>
        </div>
      </transition>
    </div>
    <div class="ui-grid__pagination" ref="pagination" v-if="showPagination">
      <div class="ui-grid__pagination-inner">
        <ui-pagination
          :page-index="tablePageIndex"
          :page-size="tablePageSize"
          :total="tableTotal"
          :title="title"
          @page-change="handlePageChange"
        >
          <template slot="addon">
            <span
              class="ui-grid__refresh"
              :class="{'is-loading': tableLoading}"
              title="刷新"
              @click="handleRefreshClick"
            ></span>
          </template>
        </ui-pagination>
      </div>
    </div>
  </div>
</template>
<script>
import { Filter, FilterItem } from '../filter'
import GridTable from './grid-table'
import GridColumn from './grid-column.vue'
import GridRowToolbar from './grid-toolbar/grid-row-toolbar'
import GridGlobalToolbar from './grid-toolbar/grid-global-toolbar'
import GridBatchToolbar from './grid-toolbar/grid-batch-toolbar'
import GridMoreToolbar from './grid-toolbar/grid-more-toolbar'
import { GridSort } from './grid-sort'
import Pagination from '../pagination'
import Loading from '../loading'
import ExportExcelJs from 'js-export-excel'
import { Column } from 'vxe-table'
import { dispatch } from './util'
import { addResizeListener, removeResizeListener } from '../../utils/resize-event'

export default {
  name: 'UiGrid',
  inject: {
    UiPage: {
      default: null
    },
    UiDialog: {
      default: null
    }
  },
  provide() {
    return {
      UiGrid: this
    }
  },
  components: {
    UiLoading: Loading,
    UiFilter: Filter,
    UiFilterItem: FilterItem,
    UiPagination: Pagination,
    VxeColumn: Column,
    GridSort,
    GridColumn,
    GridRowToolbar,
    GridGlobalToolbar,
    GridBatchToolbar,
    GridMoreToolbar,
    GridTable
  },
  props: {
    height: {
      type: String,
      default: '100%'
    },
    maxHeight: {
      type: String
    },
    selectable: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: true
    },
    emptyText: {
      type: String
    },
    data: {
      type: Array
    },
    rowToolbar: {
      type: Array
    },
    idField: {
      type: String
    },
    parentField: {
      type: String
    },
    rowToolbarWidth: {
      type: [String, Number],
      default: '120px'
    },
    loading: {
      type: Boolean,
      default: false
    },
    queryMethod: {
      type: Function
    },
    exportMethod: {
      type: Function
    },
    showPagination: {
      type: Boolean,
      default: true
    },
    showIndex: {
      type: Boolean
    },
    title: {
      type: String
    },
    total: {
      type: Number
    },
    border: {
      type: Boolean,
      default: false
    },
    showSummary: {
      type: Boolean
    },
    summaryMethod: {
      type: Function
    },
    autoLoad: {
      type: Boolean,
      default: true
    },
    rowHeight: {
      type: String,
      default: '48px'
    }
  },
  data() {
    return {
      ready: false,
      filterReady: false,
      selecteds: [],
      globalToolbarProps: {},
      batchToolbarProps: {},
      moreToolbarProps: {},
      sortProps: {
        items: [],
        value: null
      },
      filterProps: {
        items: [],
        value: null
      },
      showColumnSetting: false,
      tableColumns: [],
      tablePageIndex: 1,
      tablePageSize: 30,
      tableLoading: this.loading,
      tableTotal: 0,
      tableData: this.data,
      tableStyle: null,
      tableMaxHeight: null,
      innerIdField: this.idField,
      innerSelectable: this.selectable,
      innerMultiple: this.multiple
    }
  },
  computed: {
    showWrap() {
      if (this.globalToolbarProps && this.globalToolbarProps.items && this.globalToolbarProps.items.length) {
        return true
      }
      if (this.moreToolbarProps && this.moreToolbarProps.items && this.moreToolbarProps.items.length) {
        return true
      }
      if (this.simpleFilterItems && this.simpleFilterItems.length) {
        return true
      }
      return false
    },
    gridStyle() {
      const style = {}
      if (this.height && this.height !== 'auto') {
        style.height = this.height
      }
      if (this.height && this.maxHeight) {
        style['max-height'] = this.maxHeight
      }
      return style
    },
    simpleFilterItems() {
      if (!this.filterProps || !this.filterProps.items) {
        return
      }
      return (this.filterProps.items || []).filter(v => v.type !== 'advance')
    },
    innerShowSort() {
      return !!(this.sortProps && this.sortProps.items && this.sortProps.items.length)
    },
    exportColumns() {
      return this.tableColumns.filter(v => v.props.exportable)
    },
    tableHeight () {
      if (this.maxHeight) {
        return ''
      }
      if (!this.height || this.height === 'auto') {
        return 'auto'
      }
      return '100%'
    }
  },
  watch: {
    data(data) {
      this.tableData = data
    },
    loading(val) {
      this.tableLoading = val
    },
    idField (val) {
      this.innerIdField = val
    },
    multiple (val) {
      this.setMultiple(val)
    },
    selectable (val) {
      this.setSelectable(val)
    }
  },
  created() {
    if (this.$route.query && this.$route.query.pageindex) {
      this.tablePageIndex = Number(this.$route.query.pageindex)
    }
    dispatch(this, 'UiGridCreated', {
      setIdField: (idField) => {
        this.setIdField(idField)
      },
      setSelectable: (selectable) => {
        this.setSelectable(selectable)
      },
      setMultiple: (multiple) => {
        this.setMultiple(multiple)
      }
    })
  },
  mounted() {
    this.ready = true
    this.filterReady = true
    this.doLayout()
    // 稍微晚一点执行，页面可能还需要加载一些数据
    this.loadTimer = setTimeout(() => {
      if (this.autoLoad) {
        this.loadData()
      }
    })
    addResizeListener(this.$el.parentNode, () => {
      this.doLayout()
    })
  },
  methods: {
    setIdField (idField) {
      this.innerIdField = idField
    },
    setSelectable (selectable) {
      this.innerSelectable = selectable
    },
    setMultiple (multiple) {
      this.innerMultiple = multiple
    },
    exportToExecl(fileName) {
      return new Promise((resolve, reject) => {
        const exportColumns = this.exportColumns
        console.log(exportColumns)
        if (!exportColumns || !exportColumns.length) {
          return reject('缺少导出列')
        }
        let exportData = this.tableData || []
        let ps = Promise.resolve()
        if (this.exportMethod) {
          ps = ps.then(() => {
            return this.exportMethod().then(rs => {
              exportData = rs || []
            })
          })
        }
        ps.then(() => {
          const options = {
            fileName: fileName,
            datas: [{
              sheetData: exportData.map(v => {
                return exportColumns.reduce((rs, vv) => {
                  let value = v[vv.props.prop]
                  if (vv.props.formatter) {
                    value = vv.props.formatter(v, vv.props, value)
                  }
                  rs[vv.props.prop] = value
                  return rs
                }, {})
              }),
              sheetName: 'sheet',
              sheetHeader: exportColumns.map(v => v.props.label),
              columnWidths: exportColumns.map(v => 10)
            }]
          }
          const toExcel = new ExportExcelJs(options)
          toExcel.saveExcel()
          resolve()
        }, reject)
      })
    },
    handleDataLoaded () {
      this.$emit('loaded', {
        data: this.tableData
      })
    },
    handleSortChange({ value }) {
      this.sortProps.value = value
      this.$emit('sort-change', {
        value
      })
      this.loadData()
    },
    handleRenderFilterItem(item) {
      return {
        functional: true,
        render: (h, c) => {
          return item.render(h, c)
        }
      }
    },
    handleClearSelection() {
      this.selecteds = []
      this.$refs.table && this.$refs.table.clearSelection()
    },
    handleBatchSuccess() {
      this.loadData()
    },
    handleSelectionChange({ selection }) {
      this.selecteds = selection || []
      this.$emit('selection-change', {
        selection,
        data: this.tableData
      })
    },
    handleSelectChange ({ row }) {
      this.$emit('select-change', {
        row,
        data: this.tableData
      })
    },
    handlePageChange(e) {
      this.tablePageSize = e.pageSize
      this.tablePageIndex = e.pageIndex
      this.loadData()
    },
    handleFilterChange() {
      if (!this.autoLoad) {
        return
      }
      this.reload()
    },
    handleRefreshClick() {
      if (this.tableLoading) {
        return
      }
      this.loadData()
    },
    getFilters() {
      if (this.$refs.filter) {
        return this.$refs.filter.getFilters()
      }
      return {}
    },
    getSort() {
      let sort = null
      if (this.sortProps && this.sortProps.value) {
        sort = {
          [this.sortProps.value.prop]: this.sortProps.value.order
        }
      }
      return sort
    },
    reload() {
      this.tablePageIndex = 1
      return this.loadData()
    },
    loadData() {
      return new Promise((resolve, reject) => {
        if (typeof this.queryMethod === 'function') {
          let ps = {}
          const fs = this.getFilters()
          if (this.showPagination) {
            ps = {
              page: this.tablePageIndex,
              limit: this.tablePageSize
            }
          }
          const rs = this.queryMethod({
            filter: fs,
            page: ps,
            sort: this.getSort()
          })
          if (rs && rs.then) {
            this.tableLoading = true
            rs.then((data) => {
              this.tableLoading = false
              this._setData(data)
            }, (err) => {
              this.tableLoading = false
              reject(err)
            })
          } else {
            this._setData(rs)
          }
        } else {
          resolve()
        }
      })
    },
    _setData(result) {
      if (this.showPagination) {
        let total = 0
        let data = []
        if (result) {
          total = parseInt(result.total) || 0
          data = result.data || []
        }
        this.tableData = data
        this.tableTotal = total
      } else {
        this.tableData = result || []
      }
      this.$emit('filled', {
        data: this.tableData
      })
    },
    handleBatchBeforeEnter() {
      if (this.$refs.batch) {
        this.$refs.batch.style.display = 'block'
      }
    },
    handleBatchAfterLeave() {
      if (this.$refs.batch) {
        this.$refs.batch.style.display = 'none'
      }
    },
    setTableData(callback) {
      callback && callback(this.tableData)
    },
    toggleRowSelection(row, isSelected) {
      this.$refs.table.toggleRowSelection(row, isSelected)
    },
    setCurrentRow(row) {
      this.$refs.table.setCurrentRow(row)
    },
    setSelectionRows (rows) {
      this.$refs.table.setCheckboxRow(rows)
    },
    setSelectRow (row) {
      this.$refs.table.setRadioRow(row)
    },
    doLayout () {
      let tableMaxHeight = null
      if (this.maxHeight) {
        const parentHeight = this.$el.parentNode.clientHeight || 0
        const wrapHeight = this.$refs.wrap ? this.$refs.wrap.offsetHeight : 0
        const paginationHeight = this.$refs.pagination ? this.$refs.pagination.offsetHeight : 0
        if (this.maxHeight.indexOf('%')) {
          tableMaxHeight = parentHeight - wrapHeight - paginationHeight * (parseInt(this.maxHeight) / 100) + ''
        } else {
          tableMaxHeight = this.maxHeight - wrapHeight - paginationHeight + ''
        }
      }
      this.tableMaxHeight = tableMaxHeight
    }
  },
  beforeDestroy() {
    clearTimeout(this.timer)
    clearTimeout(this.loadTimer)
    clearTimeout(this._timer)
    removeResizeListener(this.$el.parentNode)
  }
}
</script>
<style lang="less">
.ui-grid{
  position: relative;
  display: flex;
  flex-direction: column;
}
.ui-grid__wrap {
  padding-bottom: 12px;
  position: relative;
}
.ui-grid__header{
  display: flex;
  flex-direction: row;
  align-items: center;
  min-height: 28px;
  position: relative;
  z-index: 3;
}
.ui-grid__sort{
  padding-top: 4px;
  padding-bottom: 4px;
}
.ui-grid__filter{
  display: flex;
  flex-direction: row;
  align-items: center;
}
.ui-grid__filter-item{
  vertical-align: top;
  margin-right: 12px;
}
.ui-grid__pagination-inner{
  padding: 9px 0;
  background-color: #fff;
  position: relative;
  z-index: 3;
}
.ui-grid-global-toolbar{
  .ui-toolbar-item{
    line-height: 1;
  }
}
.ui-grid__more-toolbar,
.ui-grid__global-toolbar{
  display: inline-block;
  margin-left: 12px;
}
.ui-grid__batch{
  position: absolute;
  left: 1px;
  right: 1px;
  top: 1px;
  height: 46px;
  overflow: hidden;
  display: none;
  z-index: 10;
}
.ui-grid__batch-inner{
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-right: 6px;
  padding-left: 6px;
  background-color: #f8f8f9;
  left: 0;
  top: 0;
  right: 0;
  height: 100%;
  transform: translateY(0);
}
.ui-grid__batch-inner.v-enter{
  transform: translateY(-30px);
}
.ui-grid__batch-inner.v-enter-active{
  transition: transform .2s;
}
.ui-grid__batch-inner.v-enter-to{
  transform: translateY(1px);
}
.ui-grid__batch-inner.v-leave{
  transform: translateY(1px);
}
.ui-grid__batch-inner.v-leave-active{
  transition: transform .2s;
}
.ui-grid__batch-inner.v-leave-to{
  transform: translateY(-42px);
}
.ui-grid__loading{
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
  background-color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  opacity: .8;
  line-height: 1em;
  &.v-enter{
    opacity: 0;
  }
  &.v-enter-active{
    transition: all .3s;
  }
  &.v-enter-active{
    opacity: 0.8;
  }
  &.v-leave{
    opacity: 0.8;
  }
  &.v-leave-active{
    transition: all .3s;
  }
  &.v-leave-active{
    opacity: 0;
  }
}
@keyframes grid-refreshing{
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(1turn);
  }
}
.ui-grid__refresh{
  margin-left: 12px;
  width: 28px;
  height: 28px;
  line-height: 28px;
  cursor: pointer;
  border-radius: 4px;
  transition: all .25s;
  text-align: center;
  &:hover {
    background-color: #f8f8f9;
  }
  &:after {
    content: '';
    width: 18px;
    height: 18px;
    display: inline-block;
    vertical-align: top;
    margin-top: 5px;
    background: url('./icons/refresh.svg') no-repeat 50% 50%;
    background-size: 16px;
  }
  &.is-loading {
    &:after {
      animation: grid-refreshing 1s linear infinite;
    }
  }
}
.ui-grid__table{
  position: relative;
  flex: 1;
  min-height: 0;
}
</style>
